// -*- mode:c++ -*-

// Copyright (c) 2010-2011,2017,2019-2020 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

let {{
    import math

    header_output = ""
    decoder_output = ""
    exec_output = ""

    class StoreInst(LoadStoreInst):
        execBase = 'Store'

        def __init__(self, mnem, post, add, writeback, size=4,
                     sign=False, user=False, flavor="normal"):
            super().__init__()

            self.name = mnem
            self.post = post
            self.add = add
            self.writeback = writeback
            self.size = size
            self.sign = sign
            self.user = user
            self.flavor = flavor
            self.instFlags = []
            if self.add:
                self.op = " +"
            else:
                self.op = " -"

            self.memFlags = []
            self.codeBlobs = { "postacc_code" : "" }

        def emitHelper(self, base = 'Memory', wbDecl = None):

            global header_output, decoder_output, exec_output

            codeBlobs = self.codeBlobs
            codeBlobs["predicate_test"] = pickPredicate(codeBlobs)
            (newHeader,
             newDecoder,
             newExec) = self.fillTemplates(self.name, self.Name, codeBlobs,
                                           self.memFlags, self.instFlags,
                                           base, wbDecl, None, False,
                                           self.size, self.sign)

            header_output += newHeader
            decoder_output += newDecoder
            exec_output += newExec

    class SrsInst(LoadStoreInst):
        execBase = 'Store'
        decConstBase = 'Srs'

        def __init__(self, mnem, post, add, writeback):
            super().__init__()
            self.name = mnem
            self.post = post
            self.add = add
            self.writeback = writeback

            self.Name = "SRS_" + storeImmClassName(post, add, writeback, 8)

        def emit(self):
            offset = 0
            if self.post != self.add:
                offset += 4
            if not self.add:
                offset -= 8

            eaCode = "EA = SpMode + %d;" % offset

            wbDiff = -8
            if self.add:
                wbDiff = 8
            accCode = '''

            auto tc = xc->tcBase();
            if (badMode32(tc, static_cast<OperatingMode>(regMode))) {
                return undefinedFault32(tc, currEL(tc));
            }

            CPSR cpsr = Cpsr;
            Mem_ud = (uint64_t)cSwap(LR_uw, cpsr.e) |
                     ((uint64_t)cSwap(Spsr_uw, cpsr.e) << 32);
            '''

            global header_output, decoder_output, exec_output

            codeBlobs = { "ea_code": eaCode,
                          "memacc_code": accCode,
                          "postacc_code": "" }
            codeBlobs["predicate_test"] = pickPredicate(codeBlobs)

            wbDecl = None
            if self.writeback:
                wbDecl = '''MicroAddiUop(machInst,
                              int_reg::regInMode((OperatingMode)regMode,
                                  int_reg::Sp),
                              int_reg::regInMode((OperatingMode)regMode,
                                  int_reg::Sp),
                              %d);''' % wbDiff

            (newHeader,
             newDecoder,
             newExec) = self.fillTemplates(self.name, self.Name, codeBlobs,
                 ["ArmISA::MMU::AlignWord"], [], 'SrsOp', wbDecl)

            header_output += newHeader
            decoder_output += newDecoder
            exec_output += newExec

    class StoreImmInst(StoreInst):
        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)
            self.offset = self.op + " imm"

            if self.add:
                self.wbDecl = "MicroAddiUop(machInst, base, base, imm);"
            else:
                self.wbDecl = "MicroSubiUop(machInst, base, base, imm);"

    class StoreRegInst(StoreInst):
        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)
            self.offset = self.op + " shift_rm_imm(Index, shiftAmt," + \
                                    " shiftType, OptShiftRmCondCodesC)"
            if self.add:
                 self.wbDecl = '''
                     MicroAddUop(machInst, base, base, index, shiftAmt, shiftType);
                 '''
            else:
                 self.wbDecl = '''
                     MicroSubUop(machInst, base, base, index, shiftAmt, shiftType);
                 '''

    class StoreSingle(StoreInst):
        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)

            # Build the default class name
            self.Name = self.nameFunc(self.post, self.add, self.writeback,
                                      self.size, self.sign, self.user)

            # Add memory request flags where necessary
            self.memFlags.append("%d" % int(math.log(self.size, 2)))
            if self.user:
                self.memFlags.append("ArmISA::MMU::UserMode")

            if self.flavor in ("exclusive", "relex"):
                self.instFlags.append("IsStoreConditional")
                self.memFlags.append("Request::LLSC")
            elif self.flavor != "fp":
                self.memFlags.append("ArmISA::MMU::AllowUnaligned")

            if self.flavor in ("release", "relex"):
                self.instFlags.extend(["IsWriteBarrier",
                                       "IsReadBarrier"])
                self.memFlags.append("Request::RELEASE")

            # Disambiguate the class name for different flavors of stores
            if self.flavor != "normal":
                self.Name = "%s_%s" % (self.name.upper(), self.Name)

        def emit(self):
            # Address computation
            eaCode = "EA = Base"
            if not self.post:
                eaCode += self.offset
            eaCode += ";"

            if self.flavor == "fp":
                eaCode += vfpEnabledCheckCode

            self.codeBlobs["ea_code"] = eaCode

            # Code that actually handles the access
            if self.flavor == "fp":
                accCode = 'Mem%(suffix)s = cSwap(FpDest_uw, ((CPSR)Cpsr).e);'
            else:
                accCode = \
                    'Mem%(suffix)s = cSwap(Dest%(suffix)s, ((CPSR)Cpsr).e);'
            accCode = accCode % \
                { "suffix" : buildMemSuffix(self.sign, self.size) }

            self.codeBlobs["memacc_code"] = accCode

            # Push it out to the output files
            base = buildMemBase(self.basePrefix, self.post, self.writeback)
            wbDecl = None
            if self.writeback:
                wbDecl = self.wbDecl
            self.emitHelper(base, wbDecl)

    def storeImmClassName(post, add, writeback, size=4, sign=False, user=False):
        return memClassName("STORE_IMM", post, add, writeback, size, sign, user)

    class StoreImmEx(StoreImmInst, StoreSingle):
        execBase = 'StoreEx'
        decConstBase = 'StoreExImm'
        basePrefix = 'MemoryExImm'
        nameFunc = staticmethod(storeImmClassName)

        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)
            self.codeBlobs["postacc_code"] = \
                  "Result = !writeResult; SevMailbox = 1; LLSCLock = 0;"

    class StoreImm(StoreImmInst, StoreSingle):
        decConstBase = 'LoadStoreImm'
        basePrefix = 'MemoryImm'
        nameFunc = staticmethod(storeImmClassName)

    def storeRegClassName(post, add, writeback, size=4, sign=False, user=False):
        return memClassName("STORE_REG", post, add, writeback, size, sign, user)

    class StoreReg(StoreRegInst, StoreSingle):
        decConstBase = 'StoreReg'
        basePrefix = 'MemoryReg'
        nameFunc = staticmethod(storeRegClassName)

    class StoreDouble(StoreInst):
        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)

            # Build the default class name
            self.Name = self.nameFunc(self.post, self.add, self.writeback)

            # Add memory request flags where necessary
            if self.flavor in ("exclusive", "relex"):
                self.instFlags.append("IsStoreConditional")
                self.memFlags.append("Request::LLSC")
                self.memFlags.append("ArmISA::MMU::AlignDoubleWord")
            else:
                self.memFlags.append("ArmISA::MMU::AlignWord")

            if self.flavor in ("release", "relex"):
                self.instFlags.extend(["IsWriteBarrier",
                                       "IsReadBarrier"])
                self.memFlags.append("Request::RELEASE")

            # Disambiguate the class name for different flavors of stores
            if self.flavor != "normal":
                self.Name = "%s_%s" % (self.name.upper(), self.Name)

        def emit(self):
            # Address computation code
            eaCode = "EA = Base"
            if not self.post:
                eaCode += self.offset
            eaCode += ";"

            if self.flavor == "fp":
                eaCode += vfpEnabledCheckCode

            self.codeBlobs["ea_code"] = eaCode

            # Code that actually handles the access
            if self.flavor == "fp":
                accCode = '''
                uint64_t swappedMem  = (uint64_t)FpDest_uw |
                                       ((uint64_t)FpDest2_uw << 32);
                Mem_ud = cSwap(swappedMem, ((CPSR)Cpsr).e);
                '''
            else:
                accCode = '''
                CPSR cpsr = Cpsr;
                Mem_ud = (uint64_t)cSwap(Dest_uw, cpsr.e) |
                         ((uint64_t)cSwap(Dest2_uw, cpsr.e) << 32);
                '''

            self.codeBlobs["memacc_code"] = accCode

            # Push it out to the output files
            base = buildMemBase(self.basePrefix, self.post, self.writeback)
            wbDecl = None
            if self.writeback:
                wbDecl = self.wbDecl
            self.emitHelper(base, wbDecl)

    def storeDoubleImmClassName(post, add, writeback):
        return memClassName("STORE_IMMD", post, add, writeback, 4, False, False)

    class StoreDoubleImmEx(StoreImmInst, StoreDouble):
        execBase = 'StoreEx'
        decConstBase = 'StoreExDImm'
        basePrefix = 'MemoryExDImm'
        nameFunc = staticmethod(storeDoubleImmClassName)

        def __init__(self, *args, **kargs):
            super().__init__(*args, **kargs)
            self.codeBlobs["postacc_code"] = \
                  "Result = !writeResult; SevMailbox = 1; LLSCLock = 0;"

    class StoreDoubleImm(StoreImmInst, StoreDouble):
        decConstBase = 'LoadStoreDImm'
        basePrefix = 'MemoryDImm'
        nameFunc = staticmethod(storeDoubleImmClassName)

    def storeDoubleRegClassName(post, add, writeback):
        return memClassName("STORE_REGD", post, add, writeback, 4, False, False)

    class StoreDoubleReg(StoreRegInst, StoreDouble):
        decConstBase = 'StoreDReg'
        basePrefix = 'MemoryDReg'
        nameFunc = staticmethod(storeDoubleRegClassName)

    def buildStores(mnem, size=4, sign=False, user=False):
        StoreImm(mnem, True, True, True, size, sign, user).emit()
        StoreReg(mnem, True, True, True, size, sign, user).emit()
        StoreImm(mnem, True, False, True, size, sign, user).emit()
        StoreReg(mnem, True, False, True, size, sign, user).emit()
        StoreImm(mnem, False, True, True, size, sign, user).emit()
        StoreReg(mnem, False, True, True, size, sign, user).emit()
        StoreImm(mnem, False, False, True, size, sign, user).emit()
        StoreReg(mnem, False, False, True, size, sign, user).emit()
        StoreImm(mnem, False, True, False, size, sign, user).emit()
        StoreReg(mnem, False, True, False, size, sign, user).emit()
        StoreImm(mnem, False, False, False, size, sign, user).emit()
        StoreReg(mnem, False, False, False, size, sign, user).emit()

    def buildDoubleStores(mnem):
        StoreDoubleImm(mnem, True, True, True).emit()
        StoreDoubleReg(mnem, True, True, True).emit()
        StoreDoubleImm(mnem, True, False, True).emit()
        StoreDoubleReg(mnem, True, False, True).emit()
        StoreDoubleImm(mnem, False, True, True).emit()
        StoreDoubleReg(mnem, False, True, True).emit()
        StoreDoubleImm(mnem, False, False, True).emit()
        StoreDoubleReg(mnem, False, False, True).emit()
        StoreDoubleImm(mnem, False, True, False).emit()
        StoreDoubleReg(mnem, False, True, False).emit()
        StoreDoubleImm(mnem, False, False, False).emit()
        StoreDoubleReg(mnem, False, False, False).emit()

    def buildSrsStores(mnem):
        SrsInst(mnem, True, True, True).emit()
        SrsInst(mnem, True, True, False).emit()
        SrsInst(mnem, True, False, True).emit()
        SrsInst(mnem, True, False, False).emit()
        SrsInst(mnem, False, True, True).emit()
        SrsInst(mnem, False, True, False).emit()
        SrsInst(mnem, False, False, True).emit()
        SrsInst(mnem, False, False, False).emit()

    buildStores("str")
    buildStores("strt", user=True)
    buildStores("strb", size=1)
    buildStores("strbt", size=1, user=True)
    buildStores("strh", size=2)
    buildStores("strht", size=2, user=True)

    buildSrsStores("srs")

    buildDoubleStores("strd")

    StoreImmEx("strex", False, True, False, size=4,
               flavor="exclusive").emit()
    StoreImmEx("strexh", False, True, False, size=2,
               flavor="exclusive").emit()
    StoreImmEx("strexb", False, True, False, size=1,
               flavor="exclusive").emit()
    StoreDoubleImmEx("strexd", False, True, False,
                     flavor="exclusive").emit()

    StoreImm("stl", False, True, False, size=4, flavor="release").emit()
    StoreImm("stlh", False, True, False, size=2, flavor="release").emit()
    StoreImm("stlb", False, True, False, size=1, flavor="release").emit()
    StoreImmEx("stlex", False, True, False, size=4, flavor="relex").emit()
    StoreImmEx("stlexh", False, True, False, size=2, flavor="relex").emit()
    StoreImmEx("stlexb", False, True, False, size=1, flavor="relex").emit()
    StoreDoubleImmEx("stlexd", False, True, False, flavor="relex").emit()

    StoreImm("vstr", False, True, False, size=4, flavor="fp").emit()
    StoreImm("vstr", False, False, False, size=4, flavor="fp").emit()
    StoreDoubleImm("vstr", False, True, False, flavor="fp").emit()
    StoreDoubleImm("vstr", False, False, False, flavor="fp").emit()
}};
